#include "String.h"

//for counting the number of characters in utf8 string
#include <cstring>

C32String::C32String()
{
    Data.push_back(U'\0');
}

C32String::C32String(const char8_t *InUtf8String)
{   
    Data =ToUtf32(InUtf8String);
}

C32String::C32String(const char32_t *InUtf32String)
{
    int32_t Length = 0;
    while (InUtf32String[Length]!=U'\0')
    {
        ++Length;
    }
    
    Data.reserve(Length + 1);

    for (int32_t i = 0; i <= Length; ++i)
    {
        Data.push_back(InUtf32String[i]);
    }
}

C32String::C32String(char32_t InUtf32Char)
{
    Data.push_back(InUtf32Char);
    Data.push_back(U'\0');
}

C32String::C32String(int64_t InNum)
{
    char32_t Buffer[32];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InNum < 0)
    {
        IsNegative = true;
        InNum = -InNum;
    }
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);
    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.push_back(Buffer[Length - i - 1]);
    }
    Data.push_back(U'\0');
}

C32String::C32String(uint64_t InNum)
{
    char32_t Buffer[32];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } 
    while (InNum > 0);
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.push_back(Buffer[Length - i - 1]);
    }
    Data.push_back(U'\0');

}

C32String::C32String(double InNum, int32_t Precision)
{
    char32_t Buffer[32];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InNum < 0)
    {
        IsNegative = true;
        InNum = -InNum;
    }
    int64_t IntPart = (int64_t)InNum;
    do
    {
        Buffer[Length++] = U'0' + IntPart % 10;
        IntPart /= 10;
    } while (IntPart > 0);
    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.push_back(Buffer[Length - i - 1]);
    }
    Data.push_back(U'.');
    double DecimalPart = InNum - (int64_t)InNum;
    for (int32_t i = 0; i < Precision; ++i)
    {
        DecimalPart *= 10;
        Data.push_back(U'0' + (int64_t)DecimalPart);
        DecimalPart -= (int64_t)DecimalPart;
    }
    Data.push_back(U'\0');

}

C32String::C32String(bool InValue)
{
    if (InValue)
    {
        Data.reserve(5);
        Data.push_back(U't');
        Data.push_back(U'r');
        Data.push_back(U'u');
        Data.push_back(U'e');
    }
    else
    {
        Data.reserve(6);
        Data.push_back(U'f');
        Data.push_back(U'a');
        Data.push_back(U'l');
        Data.push_back(U's');
        Data.push_back(U'e');
    }
    Data.push_back(U'\0');
}

C32String::C32String(const C32String &Other)
{
    Data = Other.Data;
}

C32String::C32String(const C32String &&Other)
{
    Data = std::move(Other.Data);
}

C32String &C32String::operator=(const C32String &Other)
{
    Data = Other.Data;
    return *this;
}

C32String &C32String::operator=(const C32String &&Other)
{
    Data = std::move(Other.Data);
    return *this;
}

int64_t C32String::ToInt64() const
{
    int64_t Result = 0;
    int32_t Length = Data.size() - 1;
    int32_t StartIndex = 0;
    bool IsNegative = false;
    if (Data[0] == U'-')
    {
        IsNegative = true;
        ++StartIndex;
    }
    for (int32_t i = StartIndex; i < Length; ++i)
    {
        Result *= 10;
        Result += Data[i] - U'0';
    }
    if (IsNegative)
    {
        Result = -Result;
    }
    return Result;
}

uint64_t C32String::ToUInt64() const
{
    uint64_t Result = 0;
    int32_t Length = Data.size() - 1;
    for (int32_t i = 0; i < Length; ++i)
    {
        Result *= 10;
        Result += Data[i] - U'0';
    }
    return Result;
}

double C32String::ToDouble() const
{
    double Result = 0;
    int32_t Length = Data.size() - 1;
    int32_t StartIndex = 0;
    bool IsNegative = false;
    if (Data[0] == U'-')
    {
        IsNegative = true;
        ++StartIndex;
    }
    int32_t DotIndex = -1;
    for (int32_t i = StartIndex; i < Length; ++i)
    {
        if (Data[i] == U'.')
        {
            DotIndex = i;
            break;
        }
        Result *= 10;
        Result += Data[i] - U'0';
    }
    if (DotIndex != -1)
    {
        double DecimalPart = 0;
        for (int32_t i = DotIndex + 1; i < Length; ++i)
        {
            DecimalPart *= 10;
            DecimalPart += Data[i] - U'0';
        }
        while (DecimalPart >= 1)
        {
            DecimalPart *= 0.1;
        }
        Result += DecimalPart;
    }
    if (IsNegative)
    {
        Result = -Result;
    }
    return Result;
}

bool C32String::ToBool() const
{
    if (Data[0] == U't')
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool C32String::operator==(const C32String &Other) const
{
    return Data == Other.Data;
}

bool C32String::operator!=(const C32String &Other) const
{
    return Data != Other.Data;
}

bool C32String::operator<(const C32String &Other) const
{
    return Data < Other.Data;
}

uint64_t C32String::GetHashCode() const
{
    uint64_t Hash = 0;
    for (uint64_t i = 0; i < Data.size() - 1; ++i)
    {
        Hash = Hash * 31 + Data[i];
    }
    return Hash;
}

uint64_t C32String::GetLength() const
{
    return Data.size() - 1;
}

void C32String::Reserve(uint64_t InCharCount)
{
    Data.reserve(InCharCount + 1);
}

const char32_t *C32String::GetData() const
{
    return Data.data();
}

std::vector<char32_t> C32String::GetUnicodes() const
{
    return Data;
}

C32String C32String::operator+(const C32String &Other) const
{
    C32String Result;
    
    Result.Reserve(GetLength() + Other.GetLength());
    Result.Data.clear();    

    for(uint64_t i = 0; i < Data.size() - 1; ++i)
    {
        Result.Data.push_back(Data[i]);
    }
 
    for(uint64_t i = 0; i < Other.Data.size(); ++i)
    {
        Result.Data.push_back(Other.Data[i]);
    }

    return Result;
}

C32String &C32String::operator+=(const C32String &Other)
{
    Reserve(GetLength() + Other.GetLength());

    Data[Data.size() - 1] = Other.Data[0];

    for(uint64_t i = 1; i < Other.Data.size(); ++i)
    {
        Data.push_back(Other.Data[i]);
    }

    return *this;
}

C32String &C32String::operator+=(char32_t Other)
{
    Reserve(GetLength() + 1);

    Data[Data.size() - 1] = Other;
    Data.push_back(U'\0');

    return *this;
}

char32_t C32String::operator[](uint64_t Index) const
{
    return Data[Index]; 
}

bool C32String::IsEmpty() const
{
    return Data.size() == 1;
}

void C32String::Clear()
{
    Data.clear();
    Data.push_back(U'\0');
}

bool C32String::StartsWith(const C32String &Other) const
{
    if (Other.GetLength() > GetLength())
    {
        return false;
    }
    for (uint64_t i = 0; i < Other.GetLength(); ++i)
    {
        if (Data[i] != Other.Data[i])
        {
            return false;
        }
    }
    return true;
}

bool C32String::EndsWith(const C32String &Other) const
{
    if (Other.GetLength() > GetLength())
    {
        return false;
    }
    for (uint64_t i = 0; i < Other.GetLength(); ++i)
    {
        if (Data[GetLength() - i - 1] != Other.Data[Other.GetLength() - i - 1])
        {
            return false;
        }
    }
    return true;
}

C32String C32String::SubString(uint64_t StartIndex, uint64_t Length) const
{
    C32String Result;
    Result.Data.clear();
    Result.Reserve(Length);

    for (uint64_t i = StartIndex; i < StartIndex + Length; ++i)
    {
        Result.Data.push_back(Data[i]);
    }
    Result.Data.push_back(U'\0');
    return Result;
}

C32String C32String::SubString(uint64_t StartIndex) const
{
    return SubString(StartIndex, GetLength() - StartIndex);
}

C32String C32String::Left(uint64_t Length) const
{
    return SubString(0, Length);
}

C32String C32String::Right(uint64_t Length) const
{
    return SubString(GetLength() - Length, Length);
}

uint64_t C32String::Find(const C32String &Other, uint64_t StartIndex) const
{
    if (Other.GetLength() > GetLength())
    {
        return -1;
    }
    for (uint64_t i = StartIndex; i < GetLength() - Other.GetLength() + 1; ++i)
    {
        bool IsMatch = true;
        for (uint64_t j = 0; j < Other.GetLength(); ++j)
        {
            if (Data[i + j] != Other.Data[j])
            {
                IsMatch = false;
                break;
            }
        }
        if (IsMatch)
        {
            return i;
        }
    }
    return -1;
}

uint64_t C32String::FindLast(const C32String &Other) const
{
    if (Other.GetLength() > GetLength())
    {
        return -1;
    }
    for (uint64_t i = GetLength() - Other.GetLength(); i >= 0; --i)
    {
        bool IsMatch = true;
        for (uint64_t j = 0; j < Other.GetLength(); ++j)
        {
            if (Data[i + j] != Other.Data[j])
            {
                IsMatch = false;
                break;
            }
        }
        if (IsMatch)
        {
            return i;
        }
    }
    return -1;
}

uint64_t C32String::Find(char32_t Other, uint64_t StartIndex) const
{
    for (uint64_t i = StartIndex; i < GetLength(); ++i)
    {
        if (Data[i] == Other)
        {
            return i;
        }
    }
    return -1;
}

uint64_t C32String::FindLast(char32_t Other) const
{
    for (uint64_t i = GetLength() - 1; i >= 0; --i)
    {
        if (Data[i] == Other)
        {
            return i;
        }
    }
    return -1;
}

C32String C32String::ToLower() const
{
    C32String Result;
    Result.Reserve(GetLength());
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] >= U'A' && Data[i] <= U'Z')
        {
            Result.Data.push_back(Data[i] + U'a' - U'A');
        }
        else
        {
            Result.Data.push_back(Data[i]);
        }
    }
    Result.Data.push_back(U'\0');
    return Result;
}

C32String C32String::ToUpper() const
{
    C32String Result;
    Result.Reserve(GetLength());
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] >= U'a' && Data[i] <= U'z')
        {
            Result.Data.push_back(Data[i] + U'A' - U'a');
        }
        else
        {
            Result.Data.push_back(Data[i]);
        }
    }
    Result.Data.push_back(U'\0');
    return Result;
}

std::vector<C32String> C32String::Split(const C32String &Delimiter) const
{
    std::vector<C32String> Result;
    uint64_t StartIndex = 0;
    while (true)
    {
        uint64_t Index = Find(Delimiter, StartIndex);
        if (Index == -1)
        {
            Result.push_back(SubString(StartIndex));
            break;
        }
        else
        {
            Result.push_back(SubString(StartIndex, Index - StartIndex));
            StartIndex = Index + Delimiter.GetLength();
        }
    }
    return Result;
}

std::vector<C32String> C32String::Split(char32_t Delimiter) const
{
    std::vector<C32String> Result;
    uint64_t StartIndex = 0;
    while (true)
    {
        uint64_t Index = Find(Delimiter, StartIndex);
        if (Index == -1)
        {
            Result.push_back(SubString(StartIndex));
            break;
        }
        else
        {
            Result.push_back(SubString(StartIndex, Index - StartIndex));
            StartIndex = Index + 1;
        }
    }
    return Result;
}

C32String C32String::Trim() const
{
    uint64_t StartIndex = 0;
    while (StartIndex < GetLength() && Data[StartIndex] == U' ')
    {
        ++StartIndex;
    }
    uint64_t EndIndex = GetLength() - 1;
    while (EndIndex >= 0 && Data[EndIndex] == U' ')
    {
        --EndIndex;
    }
    return SubString(StartIndex, EndIndex - StartIndex + 1);
}

C32String C32String::Replace(const C32String &From, const C32String &To) const
{
    C32String Result;
    uint64_t StartIndex = 0;
    while (true)
    {
        uint64_t Index = Find(From, StartIndex);
        if (Index == -1)
        {
            Result += SubString(StartIndex);
            break;
        }
        else
        {
            Result += SubString(StartIndex, Index - StartIndex);
            Result += To;
            StartIndex = Index + From.GetLength();
        }
    }
    return Result;
}

C32String C32String::Replace(char32_t From, char32_t To) const
{
    C32String Result;
    Result.Reserve(GetLength());
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] == From)
        {
            Result.Data.push_back(To);
        }
        else
        {
            Result.Data.push_back(Data[i]);
        }
    }
    Result.Data.push_back(U'\0');
    return Result;
}

C32String C32String::Remove(char32_t Char) const
{
    C32String Result;
    Result.Reserve(GetLength());
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] != Char)
        {
            Result.Data.push_back(Data[i]);
        }
    }
    Result.Data.push_back(U'\0');
    return Result;
}

C32String C32String::RemoveAt(uint64_t Index, uint64_t Length) const
{
    C32String Result;
    Result.Reserve(GetLength());
    Result.Data.clear(); 
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (i < Index || i >= Index + Length)
        {
            Result.Data.push_back(Data[i]);
        }
    }
    Result.Data.push_back(U'\0');
    return Result;
}

C32String C32String::Remove(const C32String &Str) const
{
    C32String Result;
    uint64_t StartIndex = 0;
    while (true)
    {
        uint64_t Index = Find(Str, StartIndex);
        if (Index == -1)
        {
            Result += SubString(StartIndex);
            break;
        }
        else
        {
            Result += SubString(StartIndex, Index - StartIndex);
            StartIndex = Index + Str.GetLength();
        }
    }
    return Result;
}

std::vector<char32_t> C32String::ToUtf32(const char8_t *InUtf8String)
{
    std::vector<char32_t> Result;
    uint64_t Length = std::strlen(reinterpret_cast<const char *>(InUtf8String));
    Result.reserve(Length);
    for (uint64_t i = 0; i < Length;)
    {
        char32_t Char = 0;
        if ((InUtf8String[i] & 0x80) == 0)
        {
            Char = InUtf8String[i];
            ++i;
        }
        else if ((InUtf8String[i] & 0xE0) == 0xC0)
        {
            Char = (InUtf8String[i] & 0x1F) << 6;
            Char |= (InUtf8String[i + 1] & 0x3F);
            i += 2;
        }
        else if ((InUtf8String[i] & 0xF0) == 0xE0)
        {
            Char = (InUtf8String[i] & 0x0F) << 12;
            Char |= (InUtf8String[i + 1] & 0x3F) << 6;
            Char |= (InUtf8String[i + 2] & 0x3F);
            i += 3;
        }
        else if ((InUtf8String[i] & 0xF8) == 0xF0)
        {
            Char = (InUtf8String[i] & 0x07) << 18;
            Char |= (InUtf8String[i + 1] & 0x3F) << 12;
            Char |= (InUtf8String[i + 2] & 0x3F) << 6;
            Char |= (InUtf8String[i + 3] & 0x3F);
            i += 4;
        }
        Result.push_back(Char);
    }
    Result.push_back(U'\0');
    return Result;

}


std::u8string C32String::ToUtf8() const
{
    std::vector<char8_t> Result;
    Result.reserve(GetLength() * 4);
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] < 0x80)
        {
            Result.push_back(static_cast<char8_t>(Data[i]));
        }
        else if (Data[i] < 0x800)
        {
            Result.push_back(static_cast<char8_t>(0xC0 | (Data[i] >> 6)));
            Result.push_back(static_cast<char8_t>(0x80 | (Data[i] & 0x3F)));
        }
        else if (Data[i] < 0x10000)
        {
            Result.push_back(static_cast<char8_t>(0xE0 | (Data[i] >> 12)));
            Result.push_back(static_cast<char8_t>(0x80 | ((Data[i] >> 6) & 0x3F)));
            Result.push_back(static_cast<char8_t>(0x80 | (Data[i] & 0x3F)));
        }
        else
        {
            Result.push_back(static_cast<char8_t>(0xF0 | (Data[i] >> 18)));
            Result.push_back(static_cast<char8_t>(0x80 | ((Data[i] >> 12) & 0x3F)));
            Result.push_back(static_cast<char8_t>(0x80 | ((Data[i] >> 6) & 0x3F)));
            Result.push_back(static_cast<char8_t>(0x80 | (Data[i] & 0x3F)));
        }
    }
    Result.push_back('\0');
    return Result.data();
}

C32String operator+(const char32_t *Left, const C32String &Right)
{
    return C32String(Left) + Right;
}

C32String operator+(char32_t Left, const C32String &Right)
{
    C32String Result;
    Result.Reserve(Right.GetLength() + 1);
    Result.Data[0] = Left;

    for(uint64_t i = 0; i < Right.GetLength(); ++i)
    {
        Result.Data.push_back(Right.Data[i]);
    }
    
    Result.Data.push_back('\0');

    return Result;
}

